package com.dwj.auto.comment.common.security.filter;

import com.dwj.auto.comment.common.response.Response;
import com.dwj.auto.comment.common.security.UserAuthenticationEntryPointHandler;
import com.dwj.auto.comment.common.security.config.SecurityProperties;
import com.dwj.auto.comment.common.security.jwt.JWTTokenHelper;
import com.dwj.auto.comment.common.utils.MultiReadHttpServletRequest;
import com.dwj.auto.comment.common.utils.MultiReadHttpServletResponse;
import com.dwj.auto.comment.model.vo.UserVo;
import com.dwj.auto.comment.service.IUserService;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StopWatch;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author: dangweijian
 * @description: 认证拦截器
 * @create: 2020-07-10 16:50
 **/
@Slf4j
@Configuration
public class AuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private IUserService userService;
    @Autowired
    private JWTTokenHelper jwtTokenHelper;
    @Autowired
    private SecurityProperties securityProperties;
    @Autowired
    private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
        MultiReadHttpServletResponse wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
        StopWatch stopWatch = new StopWatch();
        try {
            stopWatch.start();
            String jwtToken = wrappedRequest.getHeader(securityProperties.getJwtTokenHeader());
            log.debug("后台检查令牌:{}", jwtToken);
            if (StringUtils.isNotBlank(jwtToken)) {
                // 检查token
                boolean validate = jwtTokenHelper.validateToken(jwtToken);
                if(validate){
                    Response<UserVo> response = userService.getByToken(jwtToken);
                    if (response == null || response.getData() == null) {
                        throw new BadCredentialsException("TOKEN已过期，请重新登录！");
                    }
                    // TODO 用户权限处理
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(response.getData(), null,null);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
            filterChain.doFilter(wrappedRequest, wrappedResponse);
        } catch (SignatureException | MalformedJwtException e) {
            // jwt令牌无效
            log.info("jwt令牌无效");
            SecurityContextHolder.clearContext();
            this.userAuthenticationEntryPointHandler.commence(wrappedRequest, httpServletResponse, null);
        } catch (ExpiredJwtException e) {
            // jwt令牌过期
            log.info("jwt令牌过期");
            SecurityContextHolder.clearContext();
            this.userAuthenticationEntryPointHandler.commence(wrappedRequest, httpServletResponse, null);
        } catch (AuthenticationException e) {
            //认证异常
            log.info("jwt令牌认证异常");
            SecurityContextHolder.clearContext();
            this.userAuthenticationEntryPointHandler.commence(wrappedRequest, httpServletResponse, e);
        } finally {
            stopWatch.stop();
        }
    }
}
